package logic

import (
	"sync"

	"gddgame.cc/galaxy/asteroid"
	"gddgame.cc/galaxy/asteroid/cluster"
	"gddgame.cc/galaxy/asteroid/strategy/hash"
	"gddgame.cc/galaxy/utils/cache"
	"github.com/antlabs/timer"

	"gddgame.cc/galaxy/utils/def"

	"github.com/pkg/errors"
)

var (
	NodeFormatError = errors.New("Node Format Error:  ip:port, ip can :'' or '*' ,mean local IPv4")
)

type AgentProxy interface {
	def.Logger
	timer.Timer
}

type App interface {
	Region() string
}

type BaseApp struct {
	Id string
}

func (base *BaseApp) Region() string {
	return base.Id
}

// 提供cluster多租户模式划分
type Cloud interface {
	RegionWith(app App, module string) *asteroid.Region
	ClusterWith(app App) cluster.Container
	StoreWith(app App) def.Store
	StoreWithContainer(container cluster.Container) def.Store
}

type cloud struct {
	agent   string
	layer   *asteroid.Layer
	logger  def.Logger
	cluster cluster.Engine
	mutex   sync.RWMutex

	mc *cache.MemoryCache
}

func NewCloud(agent string, layer *asteroid.Layer, cluster cluster.Engine, logger def.Logger) *cloud {
	c := &cloud{
		agent:   agent,
		layer:   layer,
		cluster: cluster,
		mutex:   sync.RWMutex{},
		logger:  logger,
		mc:      cache.Memory(logger),
	}
	return c
}
func (c *cloud) RegionWith(app App, module string) *asteroid.Region {
	application := c.agent + "." + module + "." + app.Region()
	region, ok := c.layer.Region(application)
	if !ok {
		region.SetStrategy(hash.NewStrategy())
	}
	return region
}

func (c *cloud) ClusterWith(app App) cluster.Container {
	application := c.agent + "." + "cloud" + "." + app.Region()
	c.mutex.RLock()
	container := c.cluster.Container(application)
	c.mutex.RUnlock()
	if container == nil {
		c.mutex.Lock()
		container = c.cluster.Container(application)
		if container == nil {
			container = c.cluster.Register(application, c.RegionWith(app, "cloud"))
		}
		c.mutex.Unlock()
	}
	return container
}

func (c *cloud) StoreWith(app App) def.Store {
	application := c.agent + "." + "cloud" + "." + app.Region()

	return c.mc.Store(application)
}

func (c *cloud) StoreWithContainer(container cluster.Container) def.Store {
	return c.mc.Store(container.ID())
}
